<?php

if (!class_exists('K12Serv_API_Client', false)) {
    if (file_exists(dirname(__FILE__).'/K12Serv_API_Client.class.php')) require_once dirname(__FILE__).'/K12Serv_API_Client.class.php';
    elseif (class_exists('K12Serv_Loader', false)) K12Serv_Loader::loadClass('K12Serv_API_Client');
    else throw new Exception('请先加载类 K12Serv_API_Client。');
}

/**
 * 交易服务 API 客户端类。
 *
 * @author Nio Xiao
 * @package K12Trading
 */
class K12Trading_API_Client extends K12Serv_API_Client
{
    // 交易类型
    const TYPE_PAYMENT  = 'P'; //支付
    const TYPE_SALE     = 'S'; //收入
    const TYPE_RECHARGE = 'R'; //充值
    const TYPE_WITHDRAW = 'W'; //提现
    
    public static $trade_types = array(
        self::TYPE_PAYMENT  => '支付',
        self::TYPE_SALE     => '收入',
        self::TYPE_RECHARGE => '充值',
        self::TYPE_WITHDRAW => '提现',
    );
    
    // 交易来源分类
    const SRC_RESCLOUD   = 'rescloud'; //资源云平台
    const SRC_ALIPAY     = 'alipay'; //支付宝
    const SRC_REMITTANCE = 'remittance'; //邮局汇款
    
    // 支付交易来源分类
    public static $srcs = array(
        self::SRC_RESCLOUD => '资源云平台',
        self::SRC_ALIPAY => '支付宝', 
        self::SRC_REMITTANCE => '邮局汇款',
    );
    
    // 交易状态
    const TSTATUS_PROCESSING = 'P'; //等待付款
    const TSTATUS_SUCCESS    = 'S'; //交易成功
    const TSTATUS_ERROR      = 'E'; //交易失败
    const TSTATUS_CLOSE      = 'C'; //交易关闭（未付款）
    
    public static $trade_status = array(
        self::TSTATUS_PROCESSING => '等待付款',
        self::TSTATUS_SUCCESS    => '交易成功',
        self::TSTATUS_ERROR      => '交易失败',
        self::TSTATUS_CLOSE      => '交易关闭（未付款）',
    );
    
    // 资金流向
    const MONEY_FLOW_INCOME = 1;
    const MONEY_FLOW_OUTLAY = 2;
    
    public static $money_flows = array(
        self::MONEY_FLOW_INCOME => '收入',
        self::MONEY_FLOW_OUTLAY => '支出',
    );
    
    public static $search_conds = array(
        'user_id'    => '用户名', 
        'trade_no'   => '交易号', 
        'date_start' => '开始日期', 
        'date_end'   => '结束日期', 
        'money_flow' => '资金流向', 
        'trade_type' => '交易类型', 
        'trade_src'  => '交易来源', 
        'status'     => '交易状态',
    );
    
    /**
     * 通过调用 API 接口，新建一项支付交易。
     *
     * @param string  $user_id 用户名。
     * @param integer $amount 交易金额，单位为“分”。
     * @param string  $trade_src 交易来源。
     * @param string  $trade_desc 交易描述，可以是购买的商品名称，支持基本的 HTML 代码。
     * @param string  $notify_url 交易结果通知地址。
     * @param string  $redirect_url 交易成功之后的跳转地址。
     * @return string|boolean 返回此次交易的交易号，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function newPayment($user_id, $amount, $trade_src, $trade_desc = null, $notify_url = null, $redirect_url = null)
    {
        if (!is_numeric($amount) || intval($amount) != $amount || $amount <= 0) throw new Exception('交易金额必须为大于 0 的整数。');
        $data = array(
            'action'       => 'trading.new_payment',
            'user_id'      => $user_id,
            'amount'       => $amount,
            'trade_src'    => $trade_src,
            'trade_desc'   => $trade_desc,
            'notify_url'   => $notify_url,
            'redirect_url' => $redirect_url,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['trade_no'];
    }

    /**
     * 通过调用 API 接口，执行一项收入交易业务。
     *
     * @param string  $user_id 用户名。
     * @param integer $amount 充值金额，单位为“分”。
     * @param string  $trade_src 交易来源。
     * @param string  $trade_desc 交易描述，可以是购买的商品名称，支持基本的 HTML 代码。
     * @return string|boolean 返回此次交易的交易号，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function newSale($user_id, $amount, $trade_src, $trade_desc = null)
    {
        if (!is_numeric($amount) || intval($amount) != $amount || $amount <= 0) throw new Exception('收入金额必须为大于 0 的整数。');
        $data = array(
            'action'     => 'trading.new_sale',
            'user_id'    => $user_id,
            'amount'     => $amount,
            'trade_src'  => $trade_src,
            'trade_desc' => $trade_desc,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['trade_no'];
    }
    
    /**
     * 通过调用 API 接口，获取用户账户余额。
     *
     * @param string $user_id 用户名。
     * @return integer|boolean 返回用户账户余额，单位为“分”，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function getUserBalance($user_id)
    {
        if (!$user_id) throw new Exception('用户名不能为空。');
        $data = array(
            'action'   => 'trading.get_user_balance',
            'user_id' => $user_id,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['balance'];
    }
    
    /**
     * 通过调用 API 接口，获取交易记录信息。
     *
     * @param string $trade_no 交易号。
     * @return array|boolean 返回此次交易记录信息数组，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function getTradeInfo($trade_no)
    {
        if (!$trade_no) throw new Exception('交易号不能为空。');
        $data = array(
            'action'   => 'trading.get_trade_info',
            'trade_no' => $trade_no,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['trade_info'];
    }

    /**
     * 通过调用 API 接口，执行一项充值交易业务。
     *
     * @param string  $user_id 用户名。
     * @param integer $amount 充值金额，单位为“分”。
     * @param string  $trade_src 交易来源。
     * @param string  $trade_desc 交易描述，可以是购买的商品名称，支持基本的 HTML 代码。
     * @return string|boolean 返回此次交易的交易号，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function recharge($user_id, $amount, $trade_src, $trade_desc = null)
    {
        if (!is_numeric($amount) || intval($amount) != $amount || $amount <= 0) throw new Exception('充值金额必须为大于 0 的整数。');
        $data = array(
            'action'     => 'trading.recharge',
            'user_id'    => $user_id,
            'amount'     => $amount,
            'trade_src'  => $trade_src,
            'trade_desc' => $trade_desc,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['trade_no'];
    }
    
    /**
     * 通过调用 API 接口，执行一项充值交易业务。
     *
     * @param string  $user_id 用户名。
     * @param integer $amount 充值金额，单位为“分”。
     * @param string  $trade_src 交易来源。
     * @param string  $trade_desc 交易描述，可以是购买的商品名称，支持基本的 HTML 代码。
     * @return string|boolean 返回此次交易的交易号，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function withdraw($user_id, $amount, $trade_src, $trade_desc = null)
    {
        if (!is_numeric($amount) || intval($amount) != $amount || $amount <= 0) throw new Exception('提现金额必须为大于 0 的整数。');
        $data = array(
            'action'     => 'trading.withdraw',
            'user_id'    => $user_id,
            'amount'     => $amount,
            'trade_src'  => $trade_src,
            'trade_desc' => $trade_desc,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['trade_no'];
    }    

    /**
     * 通过调用 API 接口，修改交易状态。
     *
     * @param string $trade_no 交易号。
     * @param string $status 交易状态。
     * @return boolean 返回交易状态是否修改成功，如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function changeTradeStatus($trade_no, $status)
    {
        if (!$trade_no) throw new Exception('交易号不能为空。');
        if (!isset(self::$trade_status[$status])) throw new Exception('交易状态不正确。');
        $data = array(
            'action'   => 'trading.change_trade_status',
            'trade_no' => $trade_no,
            'status'   => $status,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : (boolean)$result['result'];
    } 
    
    /**
     * 通过调用 API 接口，修改交易状态。
     *
     * @param array $conds 查询条件，支持的 key 为 'user_id', 'trade_no', 'date_start', 'date_end', 'money_flow', 'trade_type', 'trade_src', 'status'。
     * @param string $sorting 排序，目前支持 'newest'（按创建时间正排序）与 'oldest'（按创建时间倒排序）。
     * @param integer $page 当前页码。
     * @param integer $rpp 每页记录数。
     * @return array|boolean 返回交易查询结果数组，key 包含 page（页码）、pages（总页数）、total（符合条件的记录数）、rpp（每页记录数）、sorting（排序）、items（记录集合数组）；如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function searchTrade($conds = array(), $sorting = 'newest', $page = 1, $rpp = 20)
    {
        $data = array(
            'action'  => 'trading.search_trade',
            'sorting' => $sorting,
            'page'    => intval($page),
            'rpp'     => intval($rpp),
        );
        foreach (self::$search_conds as $cond_key => $cond_text) {
            $data[$cond_key] = isset($conds[$cond_key]) ? $conds[$cond_key] : null;
        }        
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['result'];
    }       
    
    /**
     * 获取交易统计报表。
     *
     * @param string $by 按什么进行统计，其值可以是 months（按月份统计）或 days（按日期统计）。
     * @param string $date_start 开始时间。
     * @param string $date_end 结束时间。
     * @return array|boolean 返回交易查询结果数组，key 为月份或日期；如果返回 false 则表示有错误，可通过 getErrorCode() 和 getErrorMessge() 获取错误信息。
     */
    public function getTradeReports($by, $date_start, $date_end)
    {
        $data = array(
            'action'  => 'trading.get_trade_reports',
            'by' => $by,
            'date_start' => $date_start,
            'date_end' => $date_end,
        );
        $result = $this->callAPI($data);
        return ($result === false) ? false : $result['result'];    
    }
}
    